{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 创建类"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "My dog's name is Willie.\n",
      "My dog is 6 years old.\n"
     ]
    }
   ],
   "source": [
    "class Dog():\n",
    "    \"\"\"一次模拟小狗的简单尝试\"\"\"\n",
    "    \n",
    "    def __init__(self,name,age):\n",
    "        \"\"\"初始化属性name和age\"\"\"\n",
    "        self.name = name\n",
    "        self.age = age\n",
    "    \n",
    "    def sit(self):\n",
    "        \"\"\"模拟小狗被命令时蹲下\"\"\"\n",
    "        print(self.name.title() + \" is now sitting!\")\n",
    "        \n",
    "    def roll_over(self):\n",
    "        \"\"\"模拟小狗被命令时打滚\"\"\"\n",
    "        print(self.name.title() + \" rolled over!\")\n",
    "        \n",
    "my_dog = Dog('willie',6)\n",
    "\n",
    "print(\"My dog's name is \" + my_dog.name.title() + '.')\n",
    "print(\"My dog is \" + str(my_dog.age) + \" years old.\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "1，这里使用的是前一个示例中编写的Dog 类。\n",
    "2，我们让Python创建一条名字为'willie' 、年龄为6 的小狗。\n",
    "3，遇到这行代码时,Python使用实参'willie' 和6 调用Dog 类中的方法__init__() 。\n",
    "4，方法__init__() 创建一个表示特定小狗的示例,并使用我们提供的值来设置属性name 和age 。方法__init__() 并未显式地包含return 语句, 但Python自动返回一个表示这条小狗的实例。我们将这个实例存储在变量my_dog 中。\n",
    "5，在这里,命名约定很有用:通常认为首字母大写的名称(如Dog )指的是类,而小写的名称(如my_dog )指的是根据类创建的实例。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 访问属性"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "要访问实例的属性,可使用句点表示法。我们编写了如下代码来访问my_dog 的属性name 的值: "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'willie'"
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "my_dog.name"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 调用方法"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "根据Dog 类创建实例后,就可以使用句点表示法来调用Dog 类中定义的任何方法。下面来让小狗蹲下和打滚: "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Willie is now sitting!\n",
      "Willie rolled over!\n"
     ]
    }
   ],
   "source": [
    "my_dog = Dog('willie',6)\n",
    "my_dog.sit()\n",
    "my_dog.roll_over()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 创建多个实例"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "可按需求根据类创建任意数量的实例。下面再创建一个名为your_dog 的实例:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "My dog's name is Willie.\n",
      "My dog is 6 years old.\n",
      "Willie is now sitting!\n",
      "\n",
      "Your dog's name is Lucy.\n",
      "Your dog is 3 years old.\n",
      "Lucy is now sitting!\n"
     ]
    }
   ],
   "source": [
    "my_dog = Dog('willie',6)\n",
    "your_dog = Dog('Lucy',3)\n",
    "\n",
    "print(\"My dog's name is \" + my_dog.name.title() + '.')\n",
    "print(\"My dog is \" + str(my_dog.age) + \" years old.\")\n",
    "my_dog.sit()\n",
    "\n",
    "print(\"\\nYour dog's name is \" + your_dog.name.title() + '.')\n",
    "print(\"Your dog is \" + str(your_dog.age) + \" years old.\")\n",
    "your_dog.sit()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 使用类和实例"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "类中的每个属性都必须有初始值,哪怕这个值是0或空字符串。在有些情况下,如设置默认值时,在方法__init__() 内指定这种初始值是可行的;如果你对某个属性这样做了,就无需包含为它提供初始值的形参。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "2016 Audi A4\n",
      "This car has 0 miles on it\n"
     ]
    }
   ],
   "source": [
    "class Car():\n",
    "    \"\"\"一次模拟汽车的简单尝试\"\"\"\n",
    "    \n",
    "    def __init__(self,make,model,year):\n",
    "        \"\"\"初始化描述汽车的属性\"\"\"\n",
    "        self.make = make\n",
    "        self.model = model\n",
    "        self.year = year\n",
    "        self.odometer_reading = 0\n",
    "    \n",
    "    def get_descriptive_name(self):\n",
    "        \"\"\"返回整洁的描述性信息\"\"\"\n",
    "        long_name = str(self.year) + ' ' + self.make + ' ' +self.model\n",
    "        return long_name.title()\n",
    "    \n",
    "    def read_odometer(self):\n",
    "        \"\"\"打印一条指出汽车里程的消息\"\"\"\n",
    "        print(\"This car has \" + str(self.odometer_reading) + \" miles on it\")\n",
    "my_new_car = Car('audi','a4',2016)\n",
    "print(my_new_car.get_descriptive_name())\n",
    "my_new_car.read_odometer()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 直接修改属性的值"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "2016 Audi A4\n",
      "This car has 23 miles on it\n"
     ]
    }
   ],
   "source": [
    "my_new_car = Car('audi','a4',2016)\n",
    "print(my_new_car.get_descriptive_name())\n",
    "\n",
    "my_new_car.odometer_reading = 23\n",
    "my_new_car.read_odometer()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 通过方法修改属性的值"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "2016 Audi A4\n",
      "This car has 23 miles on it\n"
     ]
    }
   ],
   "source": [
    "class Car():\n",
    "    \"\"\"一次模拟汽车的简单尝试\"\"\"\n",
    "    \n",
    "    def __init__(self,make,model,year):\n",
    "        \"\"\"初始化描述汽车的属性\"\"\"\n",
    "        self.make = make\n",
    "        self.model = model\n",
    "        self.year = year\n",
    "        self.odometer_reading = 0\n",
    "    \n",
    "    def get_descriptive_name(self):\n",
    "        \"\"\"返回整洁的描述性信息\"\"\"\n",
    "        long_name = str(self.year) + ' ' + self.make + ' ' +self.model\n",
    "        return long_name.title()\n",
    "    \n",
    "    def read_odometer(self):\n",
    "        \"\"\"打印一条指出汽车里程的消息\"\"\"\n",
    "        print(\"This car has \" + str(self.odometer_reading) + \" miles on it\")\n",
    "        \n",
    "    def update_odometer(self,mileage):\n",
    "        \"\"\"将里程表读书设置为指定的值\"\"\"\n",
    "        self.odometer_reading = mileage\n",
    " \n",
    "my_new_car = Car('audi','a4',2016)\n",
    "print(my_new_car.get_descriptive_name())\n",
    "\n",
    "my_new_car.update_odometer(23)\n",
    "my_new_car.read_odometer()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "2016 Audi A4\n",
      "This car has 23 miles on it\n"
     ]
    }
   ],
   "source": [
    "class Car():\n",
    "    \"\"\"一次模拟汽车的简单尝试\"\"\"\n",
    "    \n",
    "    def __init__(self,make,model,year):\n",
    "        \"\"\"初始化描述汽车的属性\"\"\"\n",
    "        self.make = make\n",
    "        self.model = model\n",
    "        self.year = year\n",
    "        self.odometer_reading = 0\n",
    "    \n",
    "    def get_descriptive_name(self):\n",
    "        \"\"\"返回整洁的描述性信息\"\"\"\n",
    "        long_name = str(self.year) + ' ' + self.make + ' ' +self.model\n",
    "        return long_name.title()\n",
    "    \n",
    "    def read_odometer(self):\n",
    "        \"\"\"打印一条指出汽车里程的消息\"\"\"\n",
    "        print(\"This car has \" + str(self.odometer_reading) + \" miles on it\")\n",
    "        \n",
    "    def update_odometer(self,mileage):\n",
    "        \"\"\"\n",
    "        将里程表读数设置为指定的值\n",
    "        禁止将里程表读书往回调\n",
    "        \"\"\"\n",
    "        if mileage >= self.odometer_reading:\n",
    "            self.odometer_reading = mileage\n",
    "        else:\n",
    "            print(\"You can't roll back an odometer!\")\n",
    "my_new_car = Car('audi','a4',2016)\n",
    "print(my_new_car.get_descriptive_name())\n",
    "\n",
    "my_new_car.update_odometer(23)\n",
    "my_new_car.read_odometer()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 通过方法对属性的值进行递增"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Car():\n",
    "    \"\"\"一次模拟汽车的简单尝试\"\"\"\n",
    "    \n",
    "    def __init__(self,make,model,year):\n",
    "        \"\"\"初始化描述汽车的属性\"\"\"\n",
    "        self.make = make\n",
    "        self.model = model\n",
    "        self.year = year\n",
    "        self.odometer_reading = 0\n",
    "    \n",
    "    def get_descriptive_name(self):\n",
    "        \"\"\"返回整洁的描述性信息\"\"\"\n",
    "        long_name = str(self.year) + ' ' + self.make + ' ' +self.model\n",
    "        return long_name.title()\n",
    "    \n",
    "    def read_odometer(self):\n",
    "        \"\"\"打印一条指出汽车里程的消息\"\"\"\n",
    "        print(\"This car has \" + str(self.odometer_reading) + \" miles on it\")\n",
    "        \n",
    "    def update_odometer(self,mileage):\n",
    "        \"\"\"\n",
    "        将里程表读数设置为指定的值\n",
    "        禁止将里程表读书往回调\n",
    "        \"\"\"\n",
    "        if mileage >= self.odometer_reading:\n",
    "            self.odometer_reading = mileage\n",
    "        else:\n",
    "            print(\"You can't roll back an odometer!\")\n",
    "            \n",
    "    def increment_odometer(self,miles):\n",
    "        \"\"\"将里程表读数增加指定的量\"\"\"\n",
    "        self.odometer_reading += miles\n",
    "\n",
    "\n",
    "my_used_car = Car('subaru', 'outback', 2013)\n",
    "print(my_used_car.get_descriptive_name())\n",
    "\n",
    "my_used_car.update_odometer(23500)\n",
    "my_used_car.read_odometer()\n",
    "\n",
    "my_used_car.increment_odometer(100)\n",
    "my_used_car.read_odometer()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 继承"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "1，创建子类的实例时,Python首先需要完成的任务是给父类的所有属性赋值。为此,子类的方法__init__() 需要父类施以援手。\n",
    "2，例如,下面来模拟电动汽车。电动汽车是一种特殊的汽车,因此我们可以在前面创建的Car 类的基础上创建新类ElectricCar ,这样我们就只需为电动汽车特有的属性和行为编写代码。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "2016 Telsa Model S\n"
     ]
    }
   ],
   "source": [
    "class Car():\n",
    "    \"\"\"一次模拟汽车的简单尝试\"\"\"\n",
    "    \n",
    "    def __init__(self,make,model,year):\n",
    "        \"\"\"初始化描述汽车的属性\"\"\"\n",
    "        self.make = make\n",
    "        self.model = model\n",
    "        self.year = year\n",
    "        self.odometer_reading = 0\n",
    "    \n",
    "    def get_descriptive_name(self):\n",
    "        \"\"\"返回整洁的描述性信息\"\"\"\n",
    "        long_name = str(self.year) + ' ' + self.make + ' ' +self.model\n",
    "        return long_name.title()\n",
    "    \n",
    "    def read_odometer(self):\n",
    "        \"\"\"打印一条指出汽车里程的消息\"\"\"\n",
    "        print(\"This car has \" + str(self.odometer_reading) + \" miles on it\")\n",
    "        \n",
    "    def update_odometer(self,mileage):\n",
    "        if mileage >= self.odometer_reading:\n",
    "            self.odometer_reading = mileage\n",
    "        else:\n",
    "            print(\"You can't roll back an odometer!\")\n",
    "            \n",
    "    def increment_odometer(self,miles):\n",
    "        self.odometer_reading += miles\n",
    "    \n",
    "class ElectricCar(Car):\n",
    "    \"\"\"电动汽车的独特之处\"\"\"\n",
    "    def __init__(self,make,model,year):\n",
    "        \"\"\"初始化父类的属性\"\"\"\n",
    "        super().__init__(make,model,year)\n",
    "        self.battery_size = 70\n",
    "\n",
    "my_tesla = ElectricCar('telsa','model s', 2016)\n",
    "print(my_tesla.get_descriptive_name())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "1，首先是Car 类的代码。\n",
    "创建子类时,父类必须包含在当前文件中,且位于子类前面。\n",
    "定义了子类ElectricCar 。定义子类时,必须在括号内指定父类的名称。方法__init__() 接受创建Car 实例所需的信息。\n",
    "2，super() 是一个特殊函数,帮助Python将父类和子类关联起来。\n",
    "这行代码让Python调用ElectricCar 的父类的方法__init__() ,让ElectricCar 实例包含父类的所有属性。父类也称为超类 (superclass),名称super因此而得名。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 给子类定义属性和方法"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "2016 Telsa Model S\n",
      "This car has a 70-kWh battery!\n"
     ]
    }
   ],
   "source": [
    "class Car():\n",
    "    \"\"\"一次模拟汽车的简单尝试\"\"\"\n",
    "    \n",
    "    def __init__(self,make,model,year):\n",
    "        \"\"\"初始化描述汽车的属性\"\"\"\n",
    "        self.make = make\n",
    "        self.model = model\n",
    "        self.year = year\n",
    "        self.odometer_reading = 0\n",
    "    \n",
    "    def get_descriptive_name(self):\n",
    "        \"\"\"返回整洁的描述性信息\"\"\"\n",
    "        long_name = str(self.year) + ' ' + self.make + ' ' +self.model\n",
    "        return long_name.title()\n",
    "    \n",
    "    def read_odometer(self):\n",
    "        \"\"\"打印一条指出汽车里程的消息\"\"\"\n",
    "        print(\"This car has \" + str(self.odometer_reading) + \" miles on it\")\n",
    "        \n",
    "    def update_odometer(self,mileage):\n",
    "        if mileage >= self.odometer_reading:\n",
    "            self.odometer_reading = mileage\n",
    "        else:\n",
    "            print(\"You can't roll back an odometer!\")\n",
    "            \n",
    "    def increment_odometer(self,miles):\n",
    "        self.odometer_reading += miles\n",
    "    \n",
    "class ElectricCar(Car):\n",
    "    \"\"\"电动汽车的独特之处\"\"\"\n",
    "    def __init__(self,make,model,year):\n",
    "        \"\"\"\n",
    "        初始化父类的属性\n",
    "        再初始化电动汽车特有的属性\n",
    "        \"\"\"\n",
    "        super().__init__(make,model,year)\n",
    "        self.battery_size = 70\n",
    "        \n",
    "    def describe_battery(self):\n",
    "        \"\"\"打印一条描述电瓶容量的消息\"\"\"\n",
    "        print(\"This car has a \" + str(self.battery_size) + \"-kWh battery!\")\n",
    "    \n",
    "my_tesla = ElectricCar('telsa','model s', 2016)\n",
    "print(my_tesla.get_descriptive_name())\n",
    "my_tesla.describe_battery()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 重写父类的方法"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "对于父类的方法,只要它不符合子类模拟的实物的行为,都可对其进行重写。为此,可在子类中定义一个这样的方法,即它与要重写的父类方法同名。这样,Python将不会考虑这个父类方法,而只关注你在子类中定义的相应方法。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "class ElectricCar(Car):\n",
    "    \"\"\"电动汽车的独特之处\"\"\"\n",
    "    def __init__(self,make,model,year):\n",
    "        \"\"\"\n",
    "        初始化父类的属性\n",
    "        再初始化电动汽车特有的属性\n",
    "        \"\"\"\n",
    "        super().__init__(make,model,year)\n",
    "        self.battery_size = 70\n",
    "        \n",
    "    def describe_battery(self):\n",
    "        \"\"\"打印一条描述电瓶容量的消息\"\"\"\n",
    "        print(\"This car has a \" + str(self.battery_size) + \"-kWh battery!\")\n",
    "    \n",
    "    def fill_gas_tank():\n",
    "            \"\"\"电动汽车没有油箱\"\"\"\n",
    "        print(\"This car doern't need a gas tank!\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 将实例作为属性"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "使用代码模拟实物时,你可能会发现自己给类添加的细节越来越多:属性和方法清单以及文件都越来越长。在这种情况下,可能需要将类的一部分作为一个独立的类提取出来。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "2016 Telsa Model S\n",
      "This car has a 70-KWh battery.\n"
     ]
    }
   ],
   "source": [
    "class Car():\n",
    "    \"\"\"一次模拟汽车的简单尝试\"\"\"\n",
    "    \n",
    "    def __init__(self,make,model,year):\n",
    "        \"\"\"初始化描述汽车的属性\"\"\"\n",
    "        self.make = make\n",
    "        self.model = model\n",
    "        self.year = year\n",
    "        self.odometer_reading = 0\n",
    "    \n",
    "    def get_descriptive_name(self):\n",
    "        \"\"\"返回整洁的描述性信息\"\"\"\n",
    "        long_name = str(self.year) + ' ' + self.make + ' ' +self.model\n",
    "        return long_name.title()\n",
    "    \n",
    "    def read_odometer(self):\n",
    "        \"\"\"打印一条指出汽车里程的消息\"\"\"\n",
    "        print(\"This car has \" + str(self.odometer_reading) + \" miles on it\")\n",
    "        \n",
    "    def update_odometer(self,mileage):\n",
    "        if mileage >= self.odometer_reading:\n",
    "            self.odometer_reading = mileage\n",
    "        else:\n",
    "            print(\"You can't roll back an odometer!\")\n",
    "            \n",
    "    def increment_odometer(self,miles):\n",
    "        self.odometer_reading += miles\n",
    "        \n",
    "class Battery():\n",
    "    \"\"\"一次模拟电动汽车的简单尝试\"\"\"\n",
    "    \n",
    "    def __init__(self,battery_size=70):\n",
    "        \"\"\"初始化电瓶的属性\"\"\"\n",
    "        self.battery_size = battery_size\n",
    "        \n",
    "    def describe_battery(self):\n",
    "        \"\"\"打印一条描述\"\"\"\n",
    "        print(\"This car has a \" + str(self.battery_size) + \"-KWh battery.\")\n",
    "        \n",
    "    \n",
    "class ElectricCar(Car):\n",
    "    \"\"\"电动汽车的独特之处\"\"\"\n",
    "    def __init__(self,make,model,year):\n",
    "        \"\"\"\n",
    "        初始化父类的属性\n",
    "        再初始化电动汽车特有的属性\n",
    "        \"\"\"\n",
    "        super().__init__(make,model,year)\n",
    "        self.battery = Battery()\n",
    "        \n",
    "    \n",
    "my_tesla = ElectricCar('telsa','model s', 2016)\n",
    "print(my_tesla.get_descriptive_name())\n",
    "my_tesla.battery.describe_battery()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "1，我们定义了一个名为Battery 的新类,它没有继承任何类。方法__init__() 除self 外,还有另一个形参battery_size 。这个形参是可选的:如果没有给它提供值,电瓶容量将被设置为70。方法describe_battery() 也移到了这个类中。\n",
    "2，在ElectricCar 类中,我们添加了一个名为self.battery 的属性。这行代码让Python创建一个新的Battery 实例(由于没有指定尺寸,因此为默认值70 ),并将该实例存储在属性self.battery 中。每当方法__init__() 被调用时,都将执行该操作;因此现在每个ElectricCar 实例都包含一个自动创建的Battery 实例。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "1，这看似做了很多额外的工作,但现在我们想多详细地描述电瓶都可以,且不会导致ElectricCar 类混乱不堪。\n",
    "2，下面再给Battery 类添加一个方法,它根据电瓶容量报告汽车的续航里程:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "2016 Telsa Model S\n",
      "This car has a 70-KWh battery.\n",
      "This car can go approximately 240 miles on a full charge.\n"
     ]
    }
   ],
   "source": [
    "class Car():\n",
    "    \"\"\"一次模拟汽车的简单尝试\"\"\"\n",
    "    \n",
    "    def __init__(self,make,model,year):\n",
    "        \"\"\"初始化描述汽车的属性\"\"\"\n",
    "        self.make = make\n",
    "        self.model = model\n",
    "        self.year = year\n",
    "        self.odometer_reading = 0\n",
    "    \n",
    "    def get_descriptive_name(self):\n",
    "        \"\"\"返回整洁的描述性信息\"\"\"\n",
    "        long_name = str(self.year) + ' ' + self.make + ' ' +self.model\n",
    "        return long_name.title()\n",
    "    \n",
    "    def read_odometer(self):\n",
    "        \"\"\"打印一条指出汽车里程的消息\"\"\"\n",
    "        print(\"This car has \" + str(self.odometer_reading) + \" miles on it\")\n",
    "        \n",
    "    def update_odometer(self,mileage):\n",
    "        if mileage >= self.odometer_reading:\n",
    "            self.odometer_reading = mileage\n",
    "        else:\n",
    "            print(\"You can't roll back an odometer!\")\n",
    "            \n",
    "    def increment_odometer(self,miles):\n",
    "        self.odometer_reading += miles\n",
    "        \n",
    "class Battery():\n",
    "    \"\"\"一次模拟电动汽车的简单尝试\"\"\"\n",
    "    \n",
    "    def __init__(self,battery_size=70):\n",
    "        \"\"\"初始化电瓶的属性\"\"\"\n",
    "        self.battery_size = battery_size\n",
    "        \n",
    "    def describe_battery(self):\n",
    "        \"\"\"打印一条描述\"\"\"\n",
    "        print(\"This car has a \" + str(self.battery_size) + \"-KWh battery.\")\n",
    "        \n",
    "    def get_range(self):\n",
    "        \"\"\"打印一条消息，指出电瓶的续航里程\"\"\"\n",
    "        if self.battery_size == 70:\n",
    "            range = 240\n",
    "        elif self.battery_size == 85:\n",
    "            range = 270\n",
    "            \n",
    "        message = \"This car can go approximately \" + str(range)\n",
    "        message += \" miles on a full charge.\"\n",
    "        print(message)\n",
    "    \n",
    "    \n",
    "class ElectricCar(Car):\n",
    "    \"\"\"电动汽车的独特之处\"\"\"\n",
    "    def __init__(self,make,model,year):\n",
    "        \"\"\"\n",
    "        初始化父类的属性\n",
    "        再初始化电动汽车特有的属性\n",
    "        \"\"\"\n",
    "        super().__init__(make,model,year)\n",
    "        self.battery = Battery()\n",
    "        \n",
    "    \n",
    "my_tesla = ElectricCar('telsa','model s', 2016)\n",
    "print(my_tesla.get_descriptive_name())\n",
    "my_tesla.battery.describe_battery()\n",
    "my_tesla.battery.get_range()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "新增的方法get_range() 做了一些简单的分析:如果电瓶的容量为70kWh,它就将续航里程设置为240英里;如果容量为85kWh,就将续航里程设置为270英里,然后报告这个值。为使用这个方法,我们也通过汽车的属性battery 来调用它。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "注：模拟较复杂的物件(如电动汽车)时,需要解决一些有趣的问题。续航里程是电瓶的属性还是汽车的属性呢?如果我们只需描述一辆汽车,那么将方法get_range() 放在Battery 类中也许是合适的;但如果要描述一家汽车制造商的整个产品线,也许应该将方法get_range() 移到ElectricCar 类中。在这种情况下,get_range() 依然根据电瓶容量来确定续航里程,但报告的是一款汽车的续航里程。我们也可以这样做:将方法get_range() 还留在Battery 类中,但向它传递一个参数,如car_model ;在这种情况下,方法get_range() 将根据电瓶容量和汽车型号报告续航里程。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "2016 Telsa Model S\n",
      "This car has a 85-KWh battery.\n",
      "This car can go approximately 270 miles on a full charge.\n"
     ]
    }
   ],
   "source": [
    "class Car():\n",
    "    \"\"\"一次模拟汽车的简单尝试\"\"\"\n",
    "    \n",
    "    def __init__(self,make,model,year):\n",
    "        \"\"\"初始化描述汽车的属性\"\"\"\n",
    "        self.make = make\n",
    "        self.model = model\n",
    "        self.year = year\n",
    "        self.odometer_reading = 0\n",
    "    \n",
    "    def get_descriptive_name(self):\n",
    "        \"\"\"返回整洁的描述性信息\"\"\"\n",
    "        long_name = str(self.year) + ' ' + self.make + ' ' +self.model\n",
    "        return long_name.title()\n",
    "    \n",
    "    def read_odometer(self):\n",
    "        \"\"\"打印一条指出汽车里程的消息\"\"\"\n",
    "        print(\"This car has \" + str(self.odometer_reading) + \" miles on it\")\n",
    "        \n",
    "    def update_odometer(self,mileage):\n",
    "        if mileage >= self.odometer_reading:\n",
    "            self.odometer_reading = mileage\n",
    "        else:\n",
    "            print(\"You can't roll back an odometer!\")\n",
    "            \n",
    "    def increment_odometer(self,miles):\n",
    "        self.odometer_reading += miles\n",
    "        \n",
    "class Battery():\n",
    "    \"\"\"一次模拟电动汽车的简单尝试\"\"\"\n",
    "    \n",
    "    def __init__(self,battery_size=70):\n",
    "        \"\"\"初始化电瓶的属性\"\"\"\n",
    "        self.battery_size = battery_size\n",
    "        \n",
    "    def describe_battery(self):\n",
    "        \"\"\"打印一条描述\"\"\"\n",
    "        print(\"This car has a \" + str(self.battery_size) + \"-KWh battery.\")\n",
    "        \n",
    "    def get_range(self,model):\n",
    "        \"\"\"打印一条消息，指出电瓶的续航里程\"\"\"\n",
    "        if self.battery_size == 70 and model == \"model s\":\n",
    "            range = 240\n",
    "        elif self.battery_size == 85 and model == \"model s\":\n",
    "            range = 270\n",
    "            \n",
    "        message = \"This car can go approximately \" + str(range)\n",
    "        message += \" miles on a full charge.\"\n",
    "        print(message)\n",
    "    \n",
    "    \n",
    "class ElectricCar(Car):\n",
    "    \"\"\"电动汽车的独特之处\"\"\"\n",
    "    def __init__(self,make,model,year):\n",
    "        \"\"\"\n",
    "        初始化父类的属性\n",
    "        再初始化电动汽车特有的属性\n",
    "        \"\"\"\n",
    "        super().__init__(make,model,year)\n",
    "        self.battery = Battery(85)\n",
    "        \n",
    "    \n",
    "my_tesla = ElectricCar('telsa','model s', 2016)\n",
    "print(my_tesla.get_descriptive_name())\n",
    "my_tesla.battery.describe_battery()\n",
    "my_tesla.battery.get_range(\"model s\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 导入类"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "随着你不断地给类添加功能,文件可能变得很长,即便你妥善地使用了继承亦如此。为遵循Python的总体理念,应让文件尽可能整洁。为在这方面提供帮助,Python允许你将类存储在模块中,然后在主程序中导入所需的模块。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "下面来创建一个只包含Car 类的模块。这让我们面临一个微妙的命名问题:在本章中,已经有一个名为car.py的文件,但这个模块也应命名为car.py,因为它包含表示汽车的代码。我们将这样解决这个命名问题:将Car 类存储在一个名为car.py的模块中,该模块将覆盖前面使用的文件car.py。从现在开始,使用该模块的程序都必须使用更具体的文件名,如my_car.py。下面是模块car.py,其中只包含Car 类的代码:\n",
    "car.py"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [],
   "source": [
    "\"\"\"一个可用于表示汽车的类\"\"\"\n",
    "\n",
    "class Car():\n",
    "    \"\"\"一次模拟汽车的简单尝试\"\"\"\n",
    "    \n",
    "    def __init__(self,make,model,year):\n",
    "        \"\"\"初始化描述汽车的属性\"\"\"\n",
    "        self.make = make\n",
    "        self.model = model\n",
    "        self.year = year\n",
    "        self.odometer_reading = 0\n",
    "    \n",
    "    def get_descriptive_name(self):\n",
    "        \"\"\"返回整洁的描述性信息\"\"\"\n",
    "        long_name = str(self.year) + ' ' + self.make + ' ' +self.model\n",
    "        return long_name.title()\n",
    "    \n",
    "    def read_odometer(self):\n",
    "        \"\"\"打印一条指出汽车里程的消息\"\"\"\n",
    "        print(\"This car has \" + str(self.odometer_reading) + \" miles on it\")\n",
    "        \n",
    "    def update_odometer(self,mileage):\n",
    "        \"\"\"\n",
    "        将里程表读数设置为指定的值\n",
    "        禁止将里程表读书往回调\n",
    "        \"\"\"\n",
    "        if mileage >= self.odometer_reading:\n",
    "            self.odometer_reading = mileage\n",
    "        else:\n",
    "            print(\"You can't roll back an odometer!\")\n",
    "            \n",
    "    def increment_odometer(self,miles):\n",
    "        \"\"\"将里程表读数增加指定的量\"\"\"\n",
    "        self.odometer_reading += miles"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "在代码的第一行，我们包含了一个模块级文档字符串,对该模块的内容做了简要的描述。你应为自己创建的每个模块都编写文档字符串。\n",
    "下面来创建另一个文件——my_car.py,在其中导入Car 类并创建其实例:\n",
    "my_car.py"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [
    {
     "ename": "ModuleNotFoundError",
     "evalue": "No module named 'car'",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mModuleNotFoundError\u001b[0m                       Traceback (most recent call last)",
      "\u001b[1;32m<ipython-input-19-f988383f8ca9>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m\u001b[0m\n\u001b[1;32m----> 1\u001b[1;33m \u001b[1;32mfrom\u001b[0m \u001b[0mcar\u001b[0m \u001b[1;32mimport\u001b[0m \u001b[0mCar\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m      2\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m      3\u001b[0m \u001b[0mmy_new_car\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mCar\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;34m'audy'\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;34m'a4'\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;36m2016\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m      4\u001b[0m \u001b[0mprint\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mmy_new_car\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mget_descriptive_name\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m      5\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n",
      "\u001b[1;31mModuleNotFoundError\u001b[0m: No module named 'car'"
     ]
    }
   ],
   "source": [
    "from car import Car\n",
    "\n",
    "my_new_car = Car('audy', 'a4', 2016)\n",
    "print(my_new_car.get_descriptive_name())\n",
    "\n",
    "my_new_car.odometer_reading = 23\n",
    "my_new_car.read_odometer()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
